package util

import (
	"bytes"
	"encoding/json"
	"fmt"
	"illuminant/config"
	"io/ioutil"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strconv"

	"gitee.com/wangyubin/gutils"
	"github.com/gin-gonic/gin"
)

type RestResp struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data"`
}

type GraphqlResp struct {
	Data   interface{} `json:"data"`
	Errors []struct {
		Message string `json:"message"`
	} `json:"errors"`
}

const (
	SUCCESS                   = 10000 // 请求成功
	FAIL                      = 10001 // 请求失败
	REQUEST_PARAM_ERROR       = 20001 // 请求参数有误
	DATA_SEARCH_ERROR         = 20002 // 检索失败
	DATA_INSERT_ERROR         = 20003 // 插入失败
	DATA_UPDATE_ERROR         = 20004 // 更新失败
	DATA_DELETE_ERROR         = 20005 // 删除失败
	UPLOAD_ERROR              = 30001 // 上传失败
	DOWNLOAD_ERROR            = 30002 // 下载失败
	FILE_GENERATE_ERROR       = 30010 // 生成文件失败
	FILE_READ_ERROR           = 30011 // 读取文件失败
	REMOTE_CLI_EXEC_ERROR     = 40001 // 远程命令执行失败
	REMOTE_CLI_TIMEOUT_ERROR  = 40002 // 远程命令执行超时
	REMOTE_DEPLOY_ERROR       = 40003 // 远程部署失败
	REMOTE_GEN_TOKEN_ERROR    = 40004 // 生成token失败
	AUTH_USRER_PASS_NOT_MATCH = 50001 // 密码不正确
	AUTH_LOGIN_TIMEOUT        = 50002 // 登录超时
	AUTH_PERMISSION_DENIED    = 50003 // 权限不足
)

func Success(c *gin.Context, msg string, data interface{}) {

	c.JSON(http.StatusOK,
		RestResp{
			Code:    SUCCESS,
			Message: msg,
			Data:    data,
		})
}

func Fail(c *gin.Context, code int, msg string, data interface{}) {

	c.JSON(http.StatusOK,
		RestResp{
			Code:    code,
			Message: msg,
			Data:    data,
		})
}

func AuthFail(c *gin.Context, code int, msg string, data interface{}) {

	c.JSON(http.StatusUnauthorized,
		RestResp{
			Code:    code,
			Message: msg,
			Data:    data,
		})
}

func Graphql2RestResp(body []byte) (*RestResp, error) {
	var gResp GraphqlResp
	var rResp RestResp

	err := json.Unmarshal(body, &gResp)
	if err != nil {
		return nil, err
	}

	if gResp.Errors != nil {
		rResp = RestResp{
			Code:    FAIL,
			Message: gResp.Errors[0].Message,
			Data:    gResp.Data,
		}
	} else {
		rResp = RestResp{
			Code:    SUCCESS,
			Message: "",
			Data:    gResp.Data,
		}
	}

	return &rResp, err
}

func RewriteBody(resp *http.Response) error {
	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	err = resp.Body.Close()
	if err != nil {
		return err
	}

	var rResp *RestResp

	rResp, err = Graphql2RestResp(b)
	if err != nil {
		return err
	}

	nb, err := json.Marshal(&rResp)
	if err != nil {
		return err
	}
	body := ioutil.NopCloser(bytes.NewReader(nb))
	resp.Body = body
	resp.ContentLength = int64(len(nb))
	resp.Header.Set("Content-Length", strconv.Itoa(len(nb)))
	return nil
}

func ReverseProxy() gin.HandlerFunc {

	conf := config.Get()
	u, err := url.Parse(conf.Graphql.Endpoint)
	gutils.CheckError(err)

	return func(c *gin.Context) {
		director := func(req *http.Request) {
			req.Header = http.Header{}
			req.URL.Scheme = u.Scheme
			req.URL.Host = u.Host
			req.URL.Path = u.Path

			req.Header.Set("Content-Type", "application/json; charset=utf-8")
			req.Header.Set("x-hasura-admin-secret", conf.Graphql.Secret)
			// fmt.Printf("req header: %v\n", req.Header)
		}
		body, err := c.GetRawData()
		if err != nil {
			fmt.Printf("err: %v\n", err)
		}
		fmt.Printf("body: %s\n", body)
		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))

		proxy := &httputil.ReverseProxy{Director: director}
		proxy.ModifyResponse = RewriteBody
		proxy.ServeHTTP(c.Writer, c.Request)
	}
}
